﻿using System;
using System.Web;
using System.Web.Hosting;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Caches.SysCache;
using NHibernate.Cfg;


namespace Wayloop.Blog.DataAccess
{
    public class SessionHandlerModule : IHttpModule
    {
        private const string ContextItemsKey = "__NhibernateSession__";
        private static readonly object FauxObjext = new object();

        private static ISessionFactory sessionFactory;
        private static string databaseFilePath;


        /// <summary>
        /// Gets the <see cref="ISession"/> for the current HTTP request.
        /// </summary>
        /// <value>The <see cref="ISession"/> instance.</value>
        public static ISession CurrentSession
        {
            get
            {
                HttpContext context = HttpContext.Current;
                ISession session = GetSessionFromContext(context);
                if (session == null) {
                    session = GetNewSession();
                    StoreSessionInContext(context, session);
                }

                return session;
            }
        }


        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application</param>
        public void Init(HttpApplication context)
        {
            context.BeginRequest += BeginRequest;
            context.EndRequest += EndRequest;

            databaseFilePath = HostingEnvironment.MapPath("~/App_Data/blog.db");
        }


        /// <summary>
        /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
        /// </summary>
        public void Dispose()
        {
        }


        private void BeginRequest(object sender, EventArgs e)
        {
            HttpContext context = GetHttpContext(sender);
            StoreSessionInContext(context, GetNewSession());
        }


        private void EndRequest(object sender, EventArgs e)
        {
            HttpContext context = GetHttpContext(sender);
            var session = context.Items[ContextItemsKey] as ISession;
            if (session == null) {
                return;
            }

            if (session.IsDirty()) {
                session.Flush();
            }

            session.Close();
        }


        private HttpContext GetHttpContext(object sender)
        {
            var application = (HttpApplication) sender;
            return application.Context;
        }


        private static ISession GetSessionFromContext(HttpContext context)
        {
            return context.Items[ContextItemsKey] as ISession;
        }


        private static void StoreSessionInContext(HttpContext context, ISession session)
        {
            context.Items[ContextItemsKey] = session;
        }


        private static ISession GetNewSession()
        {
            return GetSessionFactory().OpenSession(new UniversalDateTimeInterceptor());
        }


        private static ISessionFactory GetSessionFactory()
        {
            if (sessionFactory == null) {
                lock (FauxObjext) {
                    if (sessionFactory == null) {
                        var factory = Fluently.Configure()
                            .Database(GetDatabaseConfiguration())
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<SessionHandlerModule>())
                            .ExposeConfiguration(BuildSchema)
                            .BuildSessionFactory();

                        sessionFactory = factory;
                    }
                }
            }

            return sessionFactory;
        }


        private static SQLiteConfiguration GetDatabaseConfiguration()
        {
            return SQLiteConfiguration.Standard
                .UsingFile(databaseFilePath)
                .Cache(x => x.ProviderClass<SysCacheProvider>().UseQueryCache());
        }


        private static void BuildSchema(Configuration config)
        {
            //File.Delete(databaseFilePath);
            new MigrationRunner(config).MigrateToLastVersion();
        }
    }
}